package client;

import java.util.Vector;

import server.Entity.EntityType;
import shared.UnifiedClock;
import shared.Vec2f;
import shared.Vec2i;
import vmo.Actor;
import vmo.Actor_Player;
import vmo.Chunk;
import vmo.Collision_Interface;
import vmo.PlayerData;
import vmo.Tile;
import shared.UnifiedClock;
import vmo.Tile.TileType;
import network.Client;
import network.Packet;
import network.Packet.packettype;
import network.Packet_ActorRequest;
import network.Packet_Actors;
import network.Packet_Chunk;
import network.Packet_Player;
import network.Packet_State;
import network.Packet_Update;
import network.Player;
import network.Request;

public class Main_Sim implements Collision_Interface {

	Client m_client;
	Chunk [][] m_chunks;
	Vector <Actor> m_actors;
	int m_spamguard;
	Actor_Render_Int m_subordinate;
	public UnifiedClock m_clock;
	public int m_timestamp;
	public static Collision_Interface m_colint;
	
	int m_width,m_height;
	boolean m_world;
	
	public Actor_Player m_player;
	
	public boolean m_buildview;
	
	public void setSubordinate(Actor_Render_Int subordinate)
	{
		m_colint=this;
		m_subordinate=subordinate;
	}
	
	public Main_Sim(Client client)
	{
		m_buildview=false;
		m_spamguard=0;
		m_clock=new UnifiedClock();
		m_client=client;
		m_chunks=new Chunk[9][];
		for (int i=0;i<9;i++)
		{
			m_chunks[i]=new Chunk[9];

		}
		m_actors=new Vector<Actor>();
		m_timestamp=0;
	}
	
	public void UpdateWorld(int timestamp, float dt)
	{
		m_timestamp++;
		if (m_actors.size()>0)
		{
			for (int i=0;i<m_actors.size();i++)
			{
				m_actors.get(i).Update(dt);
			}
		}
		m_client.Update(dt);
		HandleRequests();		
		Packet p=m_client.getPacket();
		while (p!=null)
		{
			switch (p.getType())
			{
				case CHUNK:
					HandleRequests();			
				break;
				case PLAYER:
					HandlePlayer((Packet_Player)p);
				break;
				case STATE:
					HandleState((Packet_State)p);
				break;
				case ACTORS:
					HandleActor((Packet_Actors)p);
				break;
			}		
			p=m_client.getPacket();
		}


		if (timestamp%6 == 0)
		{
			if (m_player!=null)
			{
				ChunkLoader();
				SendUpdate(timestamp);
			}
		}
		if (m_actors.size()>0)
		{
			for (int i=0;i<m_actors.size();i++)
			{
				m_actors.get(i).Update(dt);
			}
		}
		
	}
	
	void HandleActor(Packet_Actors p)
	{
		for (int i=0;i<p.m_actors.length;i++)
		{
		
			for (int j=0;j<m_actors.size();j++)
			{
				if (p.m_actors[i].getUID()==m_actors.get(j).getUID())
				{
					m_actors.remove(j);
					break;
				}
			}
			m_actors.add(p.m_actors[i]);
			m_subordinate.AddActor(p.m_actors[i]);
			p.m_actors[i].m_interface=this;
		}
	}
	
	void HandleState(Packet_State p)
	{
		short []strangers=new short[32];
		int index=0;
		//read states
		
		int clock=m_timestamp-12; //past point
		int offset=p.getTimestamp()-clock;
		
		
		for (int i=0;i<p.m_states.length;i++)
		{
			boolean stranger=true;
			//check each UID against known actors
			
			for (int j=0;j<m_actors.size();j++)
			{
				Actor a=m_actors.get(j);
				if (a.getUID()==p.m_states[i].m_uid)
				{
					stranger=false;
					a.LoadRecord(offset, p.m_states[i].m_record,p.m_states[i].m_pos,p.m_states[i].m_z);
				}
				
			}
			if (stranger==true )
			{
				strangers[index]=p.m_states[i].m_uid;
				index++;
			}
		}
		if (index>0 && m_spamguard<m_timestamp-60)
		{
			m_spamguard=m_timestamp;
			m_client.Transmit(new Packet_ActorRequest(m_timestamp,strangers,(byte)index));
		}
				
	}
	
	public void Update(float dt)
	{



	}
	
	void SendUpdate(int timestamp)
	{
		//send buffered player actions
		Packet_Update p=new Packet_Update(timestamp, m_player.m_wheel.Read());
		
		m_client.Transmit(p);
		
	}
	
	void HandlePlayer(Packet_Player p)
	{
		Actor_Player a=new Actor_Player(p.m_player);
		m_player=a;
		m_width=p.m_ewidth;
		m_height=p.m_eheight;
		if (p.m_Etype==EntityType.WORLD)
		{
			m_world=true;
		}
		else
		{
			m_world=false;
		}
		a.m_interface=this;
		m_actors.add(a);
		m_subordinate.AddActor(a);
	}
	
	void HandleRequests()
	{
		Request req=m_client.getCompleteRequest();
		if (req!=null)
		{
			//figure out which chunk on the grid we can use to store the new chunk
			int tx=(int) (m_player.getPosition().x/16);
			int ty=(int) (m_player.getPosition().y/16);
			int x=(int) (req.getX()-tx); int y=(int) (req.getY()-ty);
			x+=4; y+=4;
			if (x>=0 && x<9 && y>=0 && y<9)
			{
				if (m_chunks[x][y]==null)
				{
					m_chunks[x][y]=new Chunk(false);
					m_chunks[x][y].m_position=new Vec2i(req.getX(),req.getY());
				}
				for (int i=0;i<4;i++)
				{
					Packet_Chunk pack=(Packet_Chunk) req.getPacket(i);
					pack.ToChunk(m_chunks[x][y], req.getZ());
				}
			
				m_chunks[x][y].BatchsetImage();
				m_buildview=true;
			}
			HandleRequests();
		}
	}
	
	void ChunkLoader()
	{
		int x=(int) (m_player.getPosition().x/16); int y=(int) (m_player.getPosition().y/16);
		

		
		//try to load all chunks around the player location
		for (int i=3;i<6;i++)
		{
			for (int j=3;j<6;j++)
			{
				if (y-4+j>=0 && y-4+j<m_height)
				{
					if (x-4+i>=0 && x-4+i<m_width)
					{
						if (m_chunks[i][j]==null)
						{
							//request chunk whole cloth
			
							m_client.TransmitRequest(x-4+i, y-4+j, m_player.getLayer(), m_player.getEntity());
							if (4==i && 4==y)
							{
								if (m_player.getLayer()<15)
								{
									m_client.TransmitRequest(x-4+i, y-4+j, m_player.getLayer()+1, m_player.getEntity());	
								}
								if (m_player.getLayer()>0)
								{
									m_client.TransmitRequest(x-4+i, y-4+j, m_player.getLayer()-1, m_player.getEntity());	
								}	
							}
						}
						else
						{
							//check each layer individually
							if (m_chunks[i][j].m_tiles[m_player.getLayer()]!=null)
							{
								if (m_player.getLayer()>0)
								{
									if (m_chunks[i][j].m_tiles[m_player.getLayer()-1]==null)
									{
										m_client.TransmitRequest(x-4+i, y-4+j, m_player.getLayer()-1, m_player.getEntity());	
									}
									else if (m_player.getLayer()>1)
									{
										if (m_chunks[i][j].m_tiles[m_player.getLayer()-2]==null)
										{	
											m_client.TransmitRequest(x-4+i, y-4+j, m_player.getLayer()-2, m_player.getEntity());	
										}	
									}
									
								}
								if (m_player.getLayer()<15)
								{
									m_client.TransmitRequest(x-4+i, y-4+j, m_player.getLayer()+1, m_player.getEntity());			
								}
							}
							else
							{
								m_client.TransmitRequest(x-4+i, y-4+j, m_player.getLayer(), m_player.getEntity());	
								
							}
							
						}				
					
					}
					else if (m_world)
					{
						if (m_chunks[i][j]==null)
						{
							//request chunk whole cloth
			
							m_client.TransmitRequest(x-4+i, y-4+j, m_player.getLayer(), m_player.getEntity());
							
						}
						else
						{
							//check each layer individually					
							if (m_chunks[i][j].m_tiles[m_player.getLayer()]!=null)
							{
								if (m_player.getLayer()>0)
								{
								m_client.TransmitRequest(x-4+i, y-4+j, m_player.getLayer()-1, m_player.getEntity());	
									if (m_player.getLayer()>1)
									{
										m_client.TransmitRequest(x-4+i, y-4+j, m_player.getLayer()-2, m_player.getEntity());			
									}
								}
							}
							else
							{
								m_client.TransmitRequest(x-4+i, y-4+j, m_player.getLayer(), m_player.getEntity());			
							}
							
						}								
					}
				}
				

			}
		}
	}
	
	boolean ShiftNeeded()
	{
		//finish writing this later
		if (m_chunks[4][4]!=null)
		{
			Vec2i p=m_chunks[4][4].m_position;
			int x=(int) (m_player.getPosition().x/16);
			int y=(int) (m_player.getPosition().y/16);
			if (p.x>x)
			{
				ShiftRight();
				return true;
			}
			if (p.x<x)
			{
				ShiftLeft();
				return true;	
			}
			if (p.y>y)
			{
				ShiftDown();
				return true;
			}
			if (p.y<y)
			{
				ShiftUp();
				return true;
			}
		}
		return false;
	}
	
	void ShiftLeft()
	{
		for (int i=1;i<9;i++)
		{
			m_chunks[i-1]=m_chunks[i];

		}
		m_chunks[8]=new Chunk[9];
	}
	
	void ShiftRight()
	{
		for (int i=7;i>=0;i--)
		{
			m_chunks[i+1]=m_chunks[i];

		}
		m_chunks[8]=new Chunk[9];		
	}
	
	void ShiftUp()
	{
		for (int i=0;i<9;i++)
		{
			for (int j=1;j<9;j++)
			{
				m_chunks[i][j-1]=m_chunks[i][j];
			}
			m_chunks[i][8]=null;
		}		
	}

	void ShiftDown()
	{
		for (int i=0;i<9;i++)
		{
			for (int j=7;j>=0;j--)
			{
				m_chunks[i][j+1]=m_chunks[i][j];
			}
			m_chunks[i][0]=null;
		}	
	}
	
	public void End()
	{
		m_client.Discard();
	}

	
	Vec2i ToClientCoords(Vec2f p)
	{
		
		//need to voodoo shark as they use different grids
		if (m_chunks[4][4]!=null)
		{
			int x=4+(int)p.x/16; int y=4+(int)p.y/16;
			//multiply the coordinates by 16
			//then 
			return new Vec2i(x-m_chunks[4][4].m_position.x,y-m_chunks[4][4].m_position.y);
			
		}	
		return new Vec2i(4,4);

	}
	
	@Override
	public Tile getTile(float x, float y, byte layer) {
		// TODO Auto-generated method stub
		//if out of bounds report tiletype as bounds
		if (x<0 || x>m_width*16)
		{
			return null;
		}
		if (y<0 || y>m_height*16)
		{
			return null;
		}
		int xe=(int) (x/16), ye=(int) (y/16);
		Vec2i vi=ToClientCoords(new Vec2f(x,y));
		Chunk c=m_chunks[vi.x][vi.y];
		if (c!=null)
		{
			int xc=(int) (x%16);
			int yc=(int) (y%16);
			if (c.m_tiles[layer]!=null)
			{
				return c.m_tiles[layer][xc][yc];              
			}
			else
			{
				m_client.TransmitRequest(vi.x, vi.y, m_player.getLayer()+1, m_player.getEntity());
				return null;
			}
		}
		return null;
	}

	@Override
	public void ForceRebuild() {
		// TODO Auto-generated method stub
		m_buildview=true;
	}
}
